"""
资源监控和性能阈值管理模块
用于防止排列算法测试消耗过多系统资源
"""

import time
import psutil
import threading
import math
from typing import Dict, Any, Optional, Callable
from dataclasses import dataclass


@dataclass
class ResourceLimits:
    """资源限制配置"""
    max_memory_mb: float = 500.0           # 最大内存使用（MB）
    max_cpu_percent: float = 80.0          # 最大CPU使用率（%）
    max_execution_time: float = 30.0       # 最大执行时间（秒）
    max_string_length: int = 8             # 最大字符串长度
    max_factorial: int = 40320             # 最大阶乘值（8! = 40320）
    check_interval: float = 0.1            # 监控检查间隔（秒）


@dataclass
class ResourceUsage:
    """资源使用情况"""
    memory_mb: float = 0.0
    cpu_percent: float = 0.0
    execution_time: float = 0.0
    peak_memory_mb: float = 0.0
    exceeded_limits: list = None
    
    def __post_init__(self):
        if self.exceeded_limits is None:
            self.exceeded_limits = []


class ResourceMonitor:
    """资源监控器"""
    
    def __init__(self, limits: Optional[ResourceLimits] = None):
        self.limits = limits or ResourceLimits()
        self.monitoring = False
        self.start_time = 0.0
        self.peak_memory = 0.0
        self.monitor_thread = None
        self.should_stop = False
        
    def calculate_theoretical_complexity(self, string_length: int) -> Dict[str, Any]:
        """计算理论复杂度"""
        n = string_length
        factorial = math.factorial(n) if n <= 20 else float('inf')
        
        # 估算内存使用（每个排列约占用 n * 8 字节，假设字符串开销）
        estimated_memory_bytes = factorial * n * 8 if factorial != float('inf') else float('inf')
        estimated_memory_mb = estimated_memory_bytes / (1024 * 1024) if estimated_memory_bytes != float('inf') else float('inf')
        
        # 估算执行时间（基于经验公式）
        if n <= 8:
            estimated_time = (factorial / 1000000) * 0.1  # 经验值
        else:
            estimated_time = float('inf')
            
        return {
            'string_length': n,
            'factorial': factorial,
            'estimated_memory_mb': estimated_memory_mb,
            'estimated_time_seconds': estimated_time,
            'is_safe': self._is_safe_to_run(n, estimated_memory_mb, estimated_time)
        }
    
    def _is_safe_to_run(self, length: int, memory_mb: float, time_seconds: float) -> bool:
        """判断是否安全执行"""
        if length > self.limits.max_string_length:
            return False
        if memory_mb > self.limits.max_memory_mb:
            return False
        if time_seconds > self.limits.max_execution_time:
            return False
        return True
    
    def should_skip_test(self, test_string: str, algorithm_name: str = "") -> tuple[bool, str]:
        """判断是否应该跳过测试"""
        complexity = self.calculate_theoretical_complexity(len(test_string))
        
        if not complexity['is_safe']:
            reasons = []
            
            if len(test_string) > self.limits.max_string_length:
                reasons.append(f"字符串长度 {len(test_string)} > 限制 {self.limits.max_string_length}")
            
            if complexity['factorial'] > self.limits.max_factorial:
                reasons.append(f"阶乘值 {complexity['factorial']} > 限制 {self.limits.max_factorial}")
            
            if complexity['estimated_memory_mb'] > self.limits.max_memory_mb:
                reasons.append(f"预估内存 {complexity['estimated_memory_mb']:.1f}MB > 限制 {self.limits.max_memory_mb}MB")
            
            if complexity['estimated_time_seconds'] > self.limits.max_execution_time:
                reasons.append(f"预估时间 {complexity['estimated_time_seconds']:.1f}s > 限制 {self.limits.max_execution_time}s")
            
            reason = f"跳过 {algorithm_name} 测试: " + "; ".join(reasons)
            return True, reason
        
        return False, ""
    
    def start_monitoring(self):
        """开始监控"""
        self.monitoring = True
        self.start_time = time.time()
        self.peak_memory = 0.0
        self.should_stop = False
        
        self.monitor_thread = threading.Thread(target=self._monitor_loop, daemon=True)
        self.monitor_thread.start()
    
    def stop_monitoring(self) -> ResourceUsage:
        """停止监控并返回使用情况"""
        self.monitoring = False
        self.should_stop = True
        
        if self.monitor_thread and self.monitor_thread.is_alive():
            self.monitor_thread.join(timeout=1.0)
        
        execution_time = time.time() - self.start_time
        current_memory = self._get_memory_usage()
        
        usage = ResourceUsage(
            memory_mb=current_memory,
            cpu_percent=psutil.cpu_percent(),
            execution_time=execution_time,
            peak_memory_mb=self.peak_memory
        )
        
        # 检查是否超过限制
        self._check_limits(usage)
        
        return usage
    
    def _monitor_loop(self):
        """监控循环"""
        while self.monitoring and not self.should_stop:
            try:
                memory_usage = self._get_memory_usage()
                self.peak_memory = max(self.peak_memory, memory_usage)
                
                # 检查是否超过限制
                if self._should_terminate():
                    self.should_stop = True
                    break
                    
                time.sleep(self.limits.check_interval)
            except Exception:
                break
    
    def _should_terminate(self) -> bool:
        """检查是否应该终止执行"""
        # 给算法足够的启动时间 (至少1秒)
        if time.time() - self.start_time < 1.0:
            return False
            
        # 检查执行时间 (给很多容错时间)
        if time.time() - self.start_time > self.limits.max_execution_time * 3.0:
            return True
        
        # 检查内存使用 (给很多容错时间)
        current_memory = self._get_memory_usage()
        if current_memory > self.limits.max_memory_mb * 5.0:
            return True
        
        # 检查CPU使用率 (只在极端情况下终止)
        cpu_usage = psutil.cpu_percent(interval=0.1)
        if cpu_usage > 95.0:  # 只有在CPU几乎满载时才终止
            return True
        
        return False
    
    def _get_memory_usage(self) -> float:
        """获取当前内存使用（MB）"""
        try:
            process = psutil.Process()
            return process.memory_info().rss / (1024 * 1024)
        except Exception:
            return 0.0
    
    def _check_limits(self, usage: ResourceUsage):
        """检查资源使用是否超过限制"""
        if usage.memory_mb > self.limits.max_memory_mb:
            usage.exceeded_limits.append(f"内存使用 {usage.memory_mb:.1f}MB > 限制 {self.limits.max_memory_mb}MB")
        
        if usage.peak_memory_mb > self.limits.max_memory_mb:
            usage.exceeded_limits.append(f"峰值内存 {usage.peak_memory_mb:.1f}MB > 限制 {self.limits.max_memory_mb}MB")
        
        if usage.execution_time > self.limits.max_execution_time:
            usage.exceeded_limits.append(f"执行时间 {usage.execution_time:.1f}s > 限制 {self.limits.max_execution_time}s")
        
        if usage.cpu_percent > self.limits.max_cpu_percent:
            usage.exceeded_limits.append(f"CPU使用率 {usage.cpu_percent:.1f}% > 限制 {self.limits.max_cpu_percent}%")


def safe_execute(func: Callable, *args, limits: Optional[ResourceLimits] = None, **kwargs) -> tuple[Any, ResourceUsage]:
    """安全执行函数，带资源监控"""
    monitor = ResourceMonitor(limits)
    
    try:
        # 记录开始时间和初始内存
        start_time = time.time()
        initial_memory = monitor._get_memory_usage()
        
        # 执行函数（不启动监控线程）
        result = func(*args, **kwargs)
        
        # 计算资源使用
        execution_time = time.time() - start_time
        current_memory = monitor._get_memory_usage()
        
        usage = ResourceUsage(
            memory_mb=current_memory,
            cpu_percent=psutil.cpu_percent(),
            execution_time=execution_time,
            peak_memory_mb=current_memory  # 简化处理
        )
        
        # 检查是否超过限制
        monitor._check_limits(usage)
        
        return result, usage
    
    except Exception as e:
        # 即使出错也要记录资源使用
        execution_time = time.time() - start_time if 'start_time' in locals() else 0
        current_memory = monitor._get_memory_usage()
        
        usage = ResourceUsage(
            memory_mb=current_memory,
            cpu_percent=psutil.cpu_percent(),
            execution_time=execution_time,
            peak_memory_mb=current_memory
        )
        
        if isinstance(e, ResourceError):
            raise
        raise RuntimeError(f"执行失败: {e}") from e


class ResourceError(Exception):
    """资源限制异常"""
    pass


def format_resource_usage(usage: ResourceUsage) -> str:
    """格式化资源使用信息"""
    lines = [
        f"执行时间: {usage.execution_time:.3f} 秒",
        f"内存使用: {usage.memory_mb:.1f} MB",
        f"峰值内存: {usage.peak_memory_mb:.1f} MB",
        f"CPU使用: {usage.cpu_percent:.1f}%"
    ]
    
    if usage.exceeded_limits:
        lines.append("⚠️  超过限制:")
        for limit in usage.exceeded_limits:
            lines.append(f"   - {limit}")
    
    return "\n".join(lines)


def get_safe_test_strings(max_length: int = 8) -> list[str]:
    """获取安全的测试字符串列表"""
    safe_strings = []
    
    for length in range(2, max_length + 1):
        test_string = ''.join(chr(ord('a') + i) for i in range(length))
        safe_strings.append(test_string)
    
    return safe_strings


# 预定义的资源限制配置
CONSERVATIVE_LIMITS = ResourceLimits(
    max_memory_mb=100.0,
    max_cpu_percent=70.0,
    max_execution_time=10.0,
    max_string_length=6,
    max_factorial=720  # 6! = 720
)

MODERATE_LIMITS = ResourceLimits(
    max_memory_mb=500.0,
    max_cpu_percent=80.0,
    max_execution_time=30.0,
    max_string_length=8,
    max_factorial=40320  # 8! = 40320
)

AGGRESSIVE_LIMITS = ResourceLimits(
    max_memory_mb=1000.0,
    max_cpu_percent=90.0,
    max_execution_time=60.0,
    max_string_length=10,
    max_factorial=3628800  # 10! = 3628800
)

if __name__ == "__main__":
    # 演示用法
    monitor = ResourceMonitor(MODERATE_LIMITS)
    
    print("🔍 资源限制演示")
    print("=" * 50)
    
    test_cases = ["ab", "abc", "abcd", "abcde", "abcdef", "abcdefg", "abcdefgh", "abcdefghi"]
    
    for test_string in test_cases:
        complexity = monitor.calculate_theoretical_complexity(len(test_string))
        should_skip, reason = monitor.should_skip_test(test_string, "演示算法")
        
        print(f"\n📊 字符串: '{test_string}' (长度: {len(test_string)})")
        print(f"   阶乘: {complexity['factorial']:,}")
        print(f"   预估内存: {complexity['estimated_memory_mb']:.1f} MB")
        print(f"   预估时间: {complexity['estimated_time_seconds']:.3f} 秒")
        
        if should_skip:
            print(f"   ❌ {reason}")
        else:
            print(f"   ✅ 安全执行")
